<?php

namespace App\Supports\Payment\Channels\Sandpay\SandpaySdk;

class SandpayCommon
{
    public $productId;
    public $body;

    public function __construct($config)
    {
        $this->initClient($config);
    }

    protected function initClient($config)
    {
        $this->sellerMid = $config['sellerMid'];
        $this->publicKeyPath = $config['publicKeyPath'];
        $this->privateKeyPath =$config['privateKeyPath'];
        $this->privateKeyPwd = $config['privateKeyPwd'];
        $this->apiUrl = 'https://cashier.sandpay.com.cn';
        $this->accessType = $config['accessType'];
        $this->channelType = $config['channelType'];
        $this->appid = $config['appid'];
        $this->mer_key = $config['mer_key'];
        $this->md5_key = $config['md5_key'];
        $this->resourceEnv = $config['resourceEnv'];
        $this->gh_static_url = $config['gh_static_url'];
        $this->plMid = '';
    }
    

    /*
    |--------------------------------------------------------------------------
    | step1.组装参数
    |--------------------------------------------------------------------------
    */
    // 参数
    protected function postData($method)
    {
        $data = array(
            'head' => array(
                'version'     => '1.0',
                'method'      => $method,
                'productId'   => $this->productId,
                'accessType'  => $this->accessType,
                'mid'         => $this->sellerMid,
                'plMid'       => $this->plMid,
                'channelType' => $this->channelType,
                'reqTime'     => date('YmdHis', time()),
            ),
            'body' => $this->body,
        );
        $postData = array(
            'charset'  => 'utf-8',
            'signType' => '01',
            'data'     => json_encode($data),
            'sign'     => $this->sign($data),
        );
        return $postData;
    }

    // 参数映射 继承类需要完善这个方法
    protected function apiMap()
    {
         return array(
            'orderPay'          => array(
                'method' => 'sandpay.trade.pay',
                'url'    => '/gateway/api/order/pay',
            ),
            'orderQuery'        => array(
                'method' => 'sandpay.trade.query',
                'url'    => '/gateway/api/order/query',
            ),
            'orderRefund'       => array(
                'method' => 'sandpay.trade.refund',
                'url'    => '/gateway/api/order/refund',
            ),
            'orderMcAutoNotice' => array(
                'method' => 'sandpay.trade.notify',
                'url'    => '/gateway/api/order/mcAutoNotice',
            ),
            'clearfileDownload' => array(
                'method' => 'sandpay.trade.download',
                'url'    => '/gateway/api/clearfile/download',
            ),
        );
    }

    /*
    |--------------------------------------------------------------------------
    | step2. 请求
    |--------------------------------------------------------------------------
    */
    // curl请求接口
    public function request($apiName)
    {
        try {
            $apiMap = $this->apiMap();

            if (!isset($apiMap[$apiName])) {
                throw new \Exception('接口名错误');
            }

            $postData = $this->postData($apiMap[$apiName]['method']);
            $url      = $this->apiUrl . $apiMap[$apiName]['url'];

            $ret    = $this->httpPost($url, $postData);

            $retAry = $this->parseResult($ret); // 格式解析

            $verify = $this->verify($retAry['data'], $retAry['sign']); // 验签

            if (!$verify) {
                throw new \Exception('返回数据验签失败');
            }
            $retUp = json_decode($retAry['data'],true);
            
            if($retUp['body']){
                $retUp['body']['credential'] = str_replace("<html><head><meta http-equiv='Content-Type' content='text/html;charset=utf-8'/></head><body>",'',$retUp['body']['credential'] );
                    $retUp['body']['credential'] = str_replace("</body>",'',$retUp['body']['credential']);
                    $retUp['body']['credential'] = str_replace("</html>",'',$retUp['body']['credential']);
                    $retUp = json_encode($retUp);
                    $retAry['data'] = $retUp;
            }
            
            return $retAry;
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

  


     // curl请求接口
    public function wapPost($data)
    {
        try {
             $data = [
                'version' => 10,
                'mer_no' =>  $this->sellerMid, //商户号
                'mer_key' => $this->mer_key, // 商户私钥通过安卓APK工具解析出来的KEY1
                'mer_order_no' => $data['mer_order_no'], //商户唯一订单号
                'create_time' => date('YmdHis'),
                'expire_time' => date('YmdHis', time()+30*60),
                'order_amt' => $data['order_amt'], //订单支付金额
                'notify_url' =>  $data['notify_url'],//订单支付异步通知
                'return_url' => $data['return_url'], //订单前端页面跳转地址
                'create_ip' => $data['create_ip'],
                'goods_name' => $data['goods_name'],
                'product_code' => $this->productId, 
                'pay_extra' => json_encode($data['pay_extra']),//resourceAppid：小程序 AppID ;resourceEnv：云开发环境 ID，云函数所需参数，如不清楚请商户群里详问杉德联调人员
                'accsplit_flag' => 'NO',
                'jump_scheme' => 'sandcash://scpay',
                'meta_option' => json_encode([["s" => "Android","n" => "wxDemo","id" => "com.pay.paytypetest","sc" => "com.pay.paytypetest"]]),
                'sign_type' => 'MD5',
                //'gh_static_url'=> $this->gh_static_url // 微信小程序H5：02010006产品必填参数，小程序云发布后生成的
            ];
            $temp = $data;
            unset($temp['goods_name']);
            unset($temp['jump_scheme']);
            unset($temp['expire_time']);
            unset($temp['product_code']);
            unset($temp['clear_cycle']);
            unset($temp['meta_option']);
            
           // file_put_contents('log.txt', date('Y-m-d H:i:s', time()) . " 签名串:" . $this->getSignContent($temp)."&key=OfVZ1x+EcLjZYClVhkD9gqlWnO3" . "\r\n", FILE_APPEND); // key对应商户私钥通过安卓APK工具解析出来的MD5KEY

            $sign = strtoupper(md5($this->getSignContent($temp)."&key=".$this->md5_key));  // key对应商户私钥通过安卓APK工具解析出来的MD5KEY
            $data['sign'] = $sign;
            unset($data['gh_static_url']);
            $query = http_build_query($data);
            $payurl = "https://sandcash.mixienet.com.cn/pay/h5/fastpayment?".$query; 

            return $payurl; // 返回支付url
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

     // curl请求接口
    public function wxPost($data)
    {
        try {
             $data = [
                'version' => 10,
                'mer_no' =>  $this->sellerMid, //商户号
                'mer_key' => $this->mer_key, // 商户私钥通过安卓APK工具解析出来的KEY1
                'mer_order_no' => $data['mer_order_no'], //商户唯一订单号
                'create_time' => date('YmdHis'),
                'expire_time' => date('YmdHis', time()+30*60),
                'order_amt' => $data['order_amt'], //订单支付金额
                'notify_url' =>  $data['notify_url'],//订单支付异步通知
                'return_url' => $data['return_url'], //订单前端页面跳转地址
                'create_ip' => $data['create_ip'],
                'goods_name' => $data['goods_name'],
                'store_id' => '000000',
                'product_code' => $this->productId, 
                'clear_cycle' => '3',
                'pay_extra' => json_encode(["resourceAppid" => $this->appid, "resourceEnv" => $this->resourceEnv ]),//resourceAppid：小程序 AppID ;resourceEnv：云开发环境 ID，云函数所需参数，如不清楚请商户群里详问杉德联调人员
                'accsplit_flag' => 'NO',
                'jump_scheme' => 'sandcash://scpay',
                'meta_option' => json_encode([["s" => "Android","n" => "wxDemo","id" => "com.pay.paytypetest","sc" => "com.pay.paytypetest"]]),
                'sign_type' => 'MD5',
                //'gh_static_url'=> $this->gh_static_url // 微信小程序H5：02010006产品必填参数，小程序云发布后生成的
            ];
            $temp = $data;
            unset($temp['goods_name']);
            unset($temp['jump_scheme']);
            unset($temp['expire_time']);
            unset($temp['product_code']);
            unset($temp['clear_cycle']);
            unset($temp['meta_option']);
            
           // file_put_contents('log.txt', date('Y-m-d H:i:s', time()) . " 签名串:" . $this->getSignContent($temp)."&key=OfVZ1x+EcLjZYClVhkD9gqlWnO3" . "\r\n", FILE_APPEND); // key对应商户私钥通过安卓APK工具解析出来的MD5KEY

            $sign = strtoupper(md5($this->getSignContent($temp)."&key=".$this->md5_key));  // key对应商户私钥通过安卓APK工具解析出来的MD5KEY
            $data['sign'] = $sign;
            unset($data['gh_static_url']);
            $query = http_build_query($data);
            $payurl = "https://sandcash.mixienet.com.cn/pay/h5/applet?".$query; 

            return $payurl; // 返回支付url
        } catch (\Exception $e) {
            return $e->getMessage();
        }
    }

     function getSignContent($params) {
        ksort($params);
    
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
    
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
    
        unset ($k, $v);
        return $stringToBeSigned;
    }
    
    function checkEmpty($value)
    {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;

        return false;
    }

    // curl. 发送请求
    protected function httpPost($url, $params)
    {
        if (empty($url) || empty($params)) {
            throw new \Exception('请求参数错误');
        }
        $params = http_build_query($params);
        try {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
            curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            $data  = curl_exec($ch);
            $err   = curl_error($ch);
            $errno = curl_errno($ch);
            if ($errno) {
                $msg = 'curl errInfo: ' . $err . ' curl errNo: ' . $errno;
                throw new \Exception($msg);
            }
            curl_close($ch);
            return $data;
        } catch (\Exception $e) {
            if ($ch) curl_close($ch);
            throw $e;
        }
    }

    // curl.解析返回数据
    protected function parseResult($result)
    {
        $arr      = array();
        $response = urldecode($result);
        $arrStr   = explode('&', $response);
        foreach ($arrStr as $str) {
            $p         = strpos($str, "=");
            $key       = substr($str, 0, $p);
            $value     = substr($str, $p + 1);
            $arr[$key] = $value;
        }

        return $arr;
    }

    // 表单请求接口
    public function form($apiName)
    {
        $apiMap = $this->apiMap();
        if (!isset($apiMap[$apiName])) {
            throw new \Exception('接口名错误');
        }
        $postData = $this->postData($apiMap[$apiName]['method']);
        $url      = $this->apiUrl . $apiMap[$apiName]['url'];

        $form = '<form action="' . $url . '" method="post">';
        foreach ($postData as $k => $v) {
            $form .= "{$k} <p><input type='text' name='{$k}' value='{$v}'></p>";
        }
        $form .= '<input type="submit" value="提交"></form>';

        return $form;
    }

    /*
    |--------------------------------------------------------------------------
    | step3.签名 + 验签
    |--------------------------------------------------------------------------
    */

    // 公钥
    private function publicKey()
    {
        try {
            $file = file_get_contents($this->publicKeyPath);
            if (!$file) {
                throw new \Exception('getPublicKey::file_get_contents ERROR');
            }
            $cert   = chunk_split(base64_encode($file), 64, "\n");
            $cert   = "-----BEGIN CERTIFICATE-----\n" . $cert . "-----END CERTIFICATE-----\n";
            $res    = openssl_pkey_get_public($cert);
            $detail = openssl_pkey_get_details($res);
            openssl_free_key($res);
            if (!$detail) {
                throw new \Exception('getPublicKey::openssl_pkey_get_details ERROR');
            }
            return $detail['key'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // 私钥
    private function privateKey()
    {
        try {
            $file = file_get_contents($this->privateKeyPath);
            if (!$file) {
                throw new \Exception('getPrivateKey::file_get_contents');
            }
            if (!openssl_pkcs12_read($file, $cert, $this->privateKeyPwd)) {
                throw new \Exception('getPrivateKey::openssl_pkcs12_read ERROR');
            }

            return $cert['pkey'];
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // 私钥加签
    protected function sign($plainText)
    {
        $plainText = json_encode($plainText);
        try {
            $resource = openssl_pkey_get_private($this->privateKey());
            $result   = openssl_sign($plainText, $sign, $resource);
            openssl_free_key($resource);
            if (!$result) throw new \Exception('sign error');
            return base64_encode($sign);
        } catch (\Exception $e) {
            throw $e;
        }
    }

    // 公钥验签
    public function verify($plainText, $sign)
    {
        $resource = openssl_pkey_get_public($this->publicKey());
        $result   = openssl_verify($plainText, base64_decode($sign), $resource);
        openssl_free_key($resource);
        if (!$result) {
            throw new \Exception('签名验证未通过,plainText:' . $plainText . '。sign:' . $sign);
        }

        return $result;
    }
}
